#include "CubicBezierPatch.h"
#include <windows.h>
#include <gl/gl.h>

CubicBezierPatch::CubicBezierPatch()
{
	mCtrlPts[0]  = Vector3(-15.0, -15.0, 0.0);
	mCtrlPts[1]  = Vector3( -5.0, -15.0, 0.0);
	mCtrlPts[2]  = Vector3(  5.0, -15.0, 0.0);
	mCtrlPts[3]  = Vector3( 15.0, -15.0, 0.0);

	mCtrlPts[4]  = Vector3(-15.0, -5.0, 0.0);
	mCtrlPts[5]  = Vector3( -5.0, -5.0, 0.0);
	mCtrlPts[6]  = Vector3(  5.0, -5.0, 0.0);
	mCtrlPts[7]  = Vector3( 15.0, -5.0, 0.0);

	mCtrlPts[8]  = Vector3(-15.0, 5.0, 0.0);
	mCtrlPts[9]  = Vector3( -5.0, 5.0, 0.0);
	mCtrlPts[10] = Vector3(  5.0, 5.0, 0.0);
	mCtrlPts[11] = Vector3( 15.0, 5.0, 0.0);

	mCtrlPts[12] = Vector3(-15.0, 15.0, 0.0);
	mCtrlPts[13] = Vector3( -5.0, 15.0, 0.0);
	mCtrlPts[14] = Vector3(  5.0, 15.0, 0.0);
	mCtrlPts[15] = Vector3( 15.0, 15.0, 0.0);

	mResU = mResV = 10;
	mVertBuf = NULL;
	mNeedUpdate = true;
}

CubicBezierPatch::CubicBezierPatch(const Vector3 ctrl_pts[16])
{
	int i;
	for(i = 0; i < 16; i++)
		mCtrlPts[i] = ctrl_pts[i];
	mResU = mResV = 10;
	mVertBuf = NULL;
	mNeedUpdate = true;
}

CubicBezierPatch::~CubicBezierPatch()
{
	if(mVertBuf)
		delete [] mVertBuf;
}

void CubicBezierPatch::_updateVertBuf()
{
	unsigned int i, j, idx;
	unsigned int u, v;
	Vector3 U, V;

	Matrix4 B(-1.0,  3.0, -3.0, 1.0,
			   3.0, -6.0,  3.0, 0.0,
			  -3.0,  3.0,  0.0, 0.0,
			   1.0,  0.0,  0.0, 0.0);
	Matrix4 Px, Py, Pz;
	Matrix4 Qx, Qy, Qz;

	if(mVertBuf)
		delete [] mVertBuf;

	u = mResU + 1;
	v = mResV + 1;

	mVertBuf = new Vector3[u * v];

	for(i = 0; i < 4; i++)	// 16 control points
	{
		for(j = 0; j < 4; j++)
		{
			Px[i][j] = mCtrlPts[i*4+j].x;
			Py[i][j] = mCtrlPts[i*4+j].y;
			Pz[i][j] = mCtrlPts[i*4+j].z;
		}
	}

	Qx = B * (Px * B.transpose());
	Qy = B * (Py * B.transpose());
	Qz = B * (Pz * B.transpose());

	for(i = 0; i < u; i++)
	{
		U[2] = static_cast<float>(i) / mResU;
		U[1] = U[2] * U[2];
		U[0] = U[1] * U[2];

		for(j = 0; j < v; j++)
		{
			V[2] = static_cast<float>(j) / mResV;
			V[1] = V[2] * V[2];
			V[0] = V[1] * V[2];

			idx = i * v + j;

			mVertBuf[idx].x = U[0] * (Qx[0][0] * V[0] + Qx[0][1] * V[1] + Qx[0][2] * V[2] + Qx[0][3]) +
							  U[1] * (Qx[1][0] * V[0] + Qx[1][1] * V[1] + Qx[1][2] * V[2] + Qx[1][3]) +
							  U[2] * (Qx[2][0] * V[0] + Qx[2][1] * V[1] + Qx[2][2] * V[2] + Qx[2][3]) +
									 (Qx[3][0] * V[0] + Qx[3][1] * V[1] + Qx[3][2] * V[2] + Qx[3][3]);
			mVertBuf[idx].y = U[0] * (Qy[0][0] * V[0] + Qy[0][1] * V[1] + Qy[0][2] * V[2] + Qy[0][3]) +
							  U[1] * (Qy[1][0] * V[0] + Qy[1][1] * V[1] + Qy[1][2] * V[2] + Qy[1][3]) +
							  U[2] * (Qy[2][0] * V[0] + Qy[2][1] * V[1] + Qy[2][2] * V[2] + Qy[2][3]) +
									 (Qy[3][0] * V[0] + Qy[3][1] * V[1] + Qy[3][2] * V[2] + Qy[3][3]);
			mVertBuf[idx].z = U[0] * (Qz[0][0] * V[0] + Qz[0][1] * V[1] + Qz[0][2] * V[2] + Qz[0][3]) +
							  U[1] * (Qz[1][0] * V[0] + Qz[1][1] * V[1] + Qz[1][2] * V[2] + Qz[1][3]) +
							  U[2] * (Qz[2][0] * V[0] + Qz[2][1] * V[1] + Qz[2][2] * V[2] + Qz[2][3]) +
									 (Qz[3][0] * V[0] + Qz[3][1] * V[1] + Qz[3][2] * V[2] + Qz[3][3]);
		}
	}

	mNeedUpdate = false;
}

void CubicBezierPatch::Draw()
{
	unsigned int i, j, u, v;
	unsigned int idx;
	Vector3 a, b, c, n;

	if(mNeedUpdate)
		_updateVertBuf();

	u = mResU + 1;
	v = mResV + 1;

	glBegin(GL_TRIANGLES);
	
	for(i = 0; i < u - 1; i++)
	{
		for(j = 0; j < v - 1; j++)
		{
			idx = i * v + j;

			a = mVertBuf[idx];
			b = mVertBuf[idx + 1];
			c = mVertBuf[idx + v];

			n = (b - a).crossProduct(c - a);
			n.normalise();
			glNormal3d(n.x, n.y, n.z);

			glTexCoord2d(static_cast<double>(j)/static_cast<double>(mResV),
						 static_cast<double>(i)/static_cast<double>(mResU));
			glVertex3d(a.x, a.y, a.z);
			
			glTexCoord2d(static_cast<double>(j+1)/static_cast<double>(mResV),
						 static_cast<double>(i)/static_cast<double>(mResU));
			glVertex3d(b.x, b.y, b.z);
			
			glTexCoord2d(static_cast<double>(j)/static_cast<double>(mResV),
						 static_cast<double>(i+1)/static_cast<double>(mResU));
			glVertex3d(c.x, c.y, c.z);

			a = mVertBuf[idx + 1];
			b = mVertBuf[idx + v + 1];
			c = mVertBuf[idx + v];

			n = (b - a).crossProduct(c - a);
			n.normalise();
			glNormal3d(n.x, n.y, n.z);

			glTexCoord2d(static_cast<double>(j+1)/static_cast<double>(mResV),
						 static_cast<double>(i)/static_cast<double>(mResU));

			glVertex3d(a.x, a.y, a.z);
			glTexCoord2d(static_cast<double>(j+1)/static_cast<double>(mResV),
						 static_cast<double>(i+1)/static_cast<double>(mResU));

			glVertex3d(b.x, b.y, b.z);
			glTexCoord2d(static_cast<double>(j)/static_cast<double>(mResV),
						 static_cast<double>(i+1)/static_cast<double>(mResU));

			glVertex3d(c.x, c.y, c.z);
		}
	}

	glEnd();
}

void CubicBezierPatch::SetCtrlPt(unsigned int id, Vector3 pt)
{
	mCtrlPts[id] = pt;
	mNeedUpdate = true;
}
